package com.gillion.grape.fros.ui.util;

import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;

import com.gillion.grape.core.Logger;

public class SpringXMLConfigMerger {
	
	private static List<Element> selectBeansElements(Document generatedDocument,SAXReader reader){
		reader.getDocumentFactory().setXPathNamespaceURIs(getBeansNamespace());
		XPath beansXPath = DocumentHelper.createXPath("//beans:bean");
		List<Element> generatedBeans = beansXPath.selectNodes(generatedDocument);
		return generatedBeans;
	}
	
	private static HashMap<String, String> getBeansNamespace(){
		HashMap<String, String> beansNamespace = new HashMap<String, String>();// 将uri存入map中
		beansNamespace.put("beans", "http://www.springframework.org/schema/beans");
		return beansNamespace;
	}

	/**
	 * 将source和target的内容进行比较,将target中存在source中不存在的节点添加到source中并返回
	 * 
	 * @param source 模板生成的配置文件
	 * @param target 已有的配置文件
	 * @return 合并后的配置文件
	 */
	public static String mergeSpringXMLConfig(String source, String target) {
		SAXReader reader = new SAXReader();
		try {
			reader.setFeature("", false);
		} catch (SAXException e) {
		}
		Document generatedDocument = XmlUtil.parseXMLString(source,reader);
		List<Element> generatedBeans = selectBeansElements(generatedDocument,reader);
		List<String> generatedBeansIdList = getBeansIDList(generatedBeans);
		Document updatedDocument = XmlUtil.parseXMLString(target,reader);
		List<Element> updatedBeans = selectBeansElements(updatedDocument,reader);
		List<String> updatedBeansIdList = getBeansIDList(updatedBeans);
		removeDuplicateBean(generatedBeansIdList, updatedBeansIdList);
		addExtendsBeans(generatedDocument, updatedDocument, updatedBeansIdList);
		mergeNoIdBeans(generatedDocument, updatedBeans);
		mergeProperties(generatedBeans, updatedDocument, updatedBeans);
		return formatXml(generatedDocument);
	}

	/**
	 * 合并手工添加的property
	 * 
	 * @param generatedBeans 模板生成的bean节点
	 * @param updatedDocument 已有的文件
	 * @param updatedBeans 
	 */
	private static void mergeProperties(List<Element> generatedBeans,
			Document updatedDocument, List<Element> updatedBeans) {
		for (Element anUpdatedBeans : updatedBeans) {
			String updatedImplBeanClassAttribute = anUpdatedBeans.attributeValue("class");
			if (updatedImplBeanClassAttribute == null){
				continue;
			}
			XPath updatedPropertiesPath = DocumentHelper
					.createXPath("//beans:bean[@class='".concat(updatedImplBeanClassAttribute).concat("']/child::*"));
			List<Element> aBeanPropertyList = updatedPropertiesPath.selectNodes(updatedDocument);

			if (aBeanPropertyList.size() == 0) {
				continue;
			}
			for (Element aGeneratedImplBean : generatedBeans) {
				String generatedImplBeanClassAttribute = aGeneratedImplBean.attributeValue("class");
				if (updatedImplBeanClassAttribute.equals(generatedImplBeanClassAttribute)) {
					// 把相应的节点拷到generatedDocument下
					for (Element aRefProperty : aBeanPropertyList) {
						if (isRefBeanExist(aGeneratedImplBean,aRefProperty)){
							continue;
						}
						aGeneratedImplBean.elements().add((Element) aRefProperty.clone());
					}
				}
			}
		}
	}
	
	private static boolean isRefBeanExist(Element implBean,Element refProperty){
		List<Element> existRefProperties = implBean.elements();
		for (Element aExistRefProperty : existRefProperties) {
			boolean exist = aExistRefProperty.attributeValue("name").equals(refProperty.attributeValue("name"));
			if (exist){
				return true;
			}
		}
		return false;
	}

	/**
	 * 合并没有id属性使用name属性的bean节点到已有的配置文件中
	 * 
	 * @param generatedDocument 已有的配置文件
	 * @param updatedBeans 合并后的bean
	 */
	private static void mergeNoIdBeans(Document generatedDocument,
			List<Element> updatedBeans) {
		for (Element anUpdatedBean : updatedBeans) {
			String updatedBeanName = anUpdatedBean.attributeValue("name");
			if (updatedBeanName == null){
				continue;
			}
			generatedDocument.getRootElement().add((Element) anUpdatedBean.clone());
		}
	}

	/**
	 * 合并手工维护的bean
	 * 
	 * @param generatedDocument 模板生成的配置文件
	 * @param updatedDocument 已有的配置文件
	 * @param updatedBeansIdList 已有配置文件的bean
	 */
	private static void addExtendsBeans(Document generatedDocument,
			Document updatedDocument, List<String> updatedBeansIdList) {
		for (String addedBeans : updatedBeansIdList) {
			XPath anAddedBeanXPath = DocumentHelper.createXPath("//beans:bean[@id='".concat(addedBeans).concat("']"));
			List<Element> anAddedBean = anAddedBeanXPath.selectNodes(updatedDocument);
			generatedDocument.getRootElement().add((Element) anAddedBean.get(0).clone());
		}
	}

	/**
	 * 删除已有文件中和生成的新配置重复的bean
	 * 
	 * @param generatedBeansIdList 模板生成的bean
	 * @param updatedBeansIdList 已有文件的bean
	 */
	private static void removeDuplicateBean(List<String> generatedBeansIdList,
			List<String> updatedBeansIdList) {
		for (int i = 0; i < generatedBeansIdList.size(); i++) {
			for (int j = 0; j < updatedBeansIdList.size(); j++) {
				if (generatedBeansIdList.get(i).equals(
						updatedBeansIdList.get(j))) {
					updatedBeansIdList.remove(j);
				}
			}
		}
	}

	private static List<String> getBeansIDList(List<Element> generatedBeans) {
		List<String> generatedBeansIdList = new ArrayList<String>();
		for (Element anGeneratedBean : generatedBeans) { // 获取generatedBeans的各个bean的id，
			String generatedBeanId = anGeneratedBean.attributeValue("id");
			if (generatedBeanId != null) {
				generatedBeansIdList.add(generatedBeanId);
			}
		}
		return generatedBeansIdList;
	}

	public static String formatXml(Document document) {
		OutputFormat outputFormat = OutputFormat.createPrettyPrint();
		outputFormat.setEncoding("UTF-8");
		Writer writer = new StringWriter();
		XMLWriter xmlWriter = new XMLWriter(writer, outputFormat);
		try {
			xmlWriter.write(document);
			xmlWriter.flush();
			xmlWriter.close();
		} catch (Exception e) {
			Logger.logError(e);
		}
		String result = writer.toString();
		return result;
	}
}
